import asyncio
import urllib
from datetime import datetime

import aiohttp
import json

import aioredis

from baseHandler import init_logger
import cx_Oracle as cx
from concurrent.futures import ThreadPoolExecutor
import hashlib
import base64
from util import aes


def oracle_sync(proc_name, input_json):
    conn = cx.connect('paydb/paydb#8bill@127.0.0.1:10086/helowin')
    cursor = conn.cursor()
    output = cursor.var(cx.STRING)
    cursor.callproc(proc_name, [input_json, output])
    return output.getvalue()


async def pay_core(proc_name, input_json):
    # executor = self.application.executor
    input_json = json.dumps(input_json)
    result = await loop.run_in_executor(executor, oracle_sync, proc_name, input_json)
    return json.loads(result)


async def orace_update(data, result_obj):
    input_json = {
        "DEPTID": "800001",
        "OPERID": "9527",
        "FUNCTYPE": "RESP",
        "FUNCCODE": "300101",
        "OPERPASS": "f15dbcb3154d37e4a183a350e4e8a15dde16a175",
        "REQINFO": {
            "OUTERORDERNO": data["OUTERORDERNO"],
            # "OPMONEY": str(core_response["OPMONEY"]),
            "PAYORDERNO": data["OUTERORDERNO"],
            "ORDERSTATE": result_obj.get('orderstate', ''),
            "PAYINFO": result_obj.get('message', ""),
        }
    }
    logger.info(input_json)
    result = await pay_core("PAY_UNIONPAY_ENTRUST_RES", input_json)
    logger.info(result)


async def http_result(url, data):
    session = aiohttp.ClientSession()
    rsp = await session.post(url, data=json.dumps(data))
    body = await rsp.text()
    session.close()
    return json.loads(body)


def encrypt(aes_key, data):
    data = json.dumps(data)
    crypto = aes.AesEncryption(aes_key)
    data = crypto.encrypt(data)
    return base64.urlsafe_b64encode(data).decode("utf-8")


def niti_data(aeskey, sha_key, notify_data, result):
    encrypt_text = encrypt(aeskey, notify_data)
    merchantid = result["MERCHANTID"]
    timestamp = result.get("RESPTIME", datetime.now().strftime('%Y%m%d%H%M%S'))
    sign_string = sha_key + merchantid + encrypt_text + timestamp + sha_key
    sign = hashlib.sha256(sign_string.encode("utf-8")).hexdigest()
    request_obj = {
        "merchantid": merchantid,
        "data": encrypt_text,
        "sign": sign,
        "timestamp": timestamp
    }
    return request_obj


async def get_key(merchantid):
    aeskey = await redis.hget(merchantid, "AESKEY", encoding='utf-8')
    shakey = await redis.hget(merchantid, "SHAKEY", encoding='utf-8')
    return aeskey, shakey

async def noti_user(data, result):
    notify_url = data.get('NOTIFYURL')
    outer_orderno = data.get("OUTERORDERNO")
    if notify_url:
        merchantid = data.get("MERCHANTID")
        aeskey, shakey = await get_key(merchantid)
        for i in range(5):
            notify_data = {
                "ordernumber": data["MERCHANTORDERNO"],
                "respcode": result["orderstate"],
                "merchantid": merchantid,
                "amount": data["OPMONEY"],
                "businesstime": ""
            }

            request_obj = niti_data(aeskey, shakey, notify_data, data)
            session = aiohttp.ClientSession()
            rsp = await session.post(notify_url, data=request_obj)
            body = await rsp.text()
            logger.info(body)
            session.close()
            # content = rsp.body.upper()
            if rsp.status == 200 and body in ('SUCCESS', 'success'):
                logger.info("%s|%s收到下游成功应答|订单号%s", i, outer_orderno, data["MERCHANTORDERNO"])
                break
            else:
                logger.info("%s|%s未收到下游成功应答|订单号%s", i, outer_orderno, data["MERCHANTORDERNO"])
                await asyncio.sleep(10 * (i + 1))


async def noti_task(data):
    channel = data['PAYCHANNEL'] + '_Q'
    url = await redis.hget('channel', channel, encoding='utf-8')
    logger.info("task begin")
    await asyncio.sleep(60)
    n = 0
    while n <= 4:
        result_obj = await http_result(url, data)
        logger.info(result_obj)
        await orace_update(data, result_obj)
        if result_obj.get("orderstate") in ("0", "1", 0, 1):
            await noti_user(data, result_obj)
            break
        else:
            n += 1
            logger.info("===" * n)
            await asyncio.sleep(5*60 * n)


async def periodic():
    while True:
        redis_1 = await aioredis.create_redis(address=('localhost', 12580), password='PayCore#8bill', db=1, loop=loop)
        data = await redis_1.brpop('settle', encoding='utf-8')
        obj = json.loads(data[1].replace("'", '"'))
        logger.info(obj)
        # asyncio.ensure_future(task(url, obj))
        loop.create_task(noti_task(obj))


if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    task = asyncio.Task(periodic())
    executor = ThreadPoolExecutor(max_workers=4)
    logger = init_logger('settle_noti', '/data/log/notify/query.log')
    redis = loop.run_until_complete(
            aioredis.create_redis(address=('localhost', 12580), password='PayCore#8bill', loop=loop)
        )
    loop.run_forever()

